/*
 * SelectionExpression.java
 * 
 * last update: 16.01.2010 by Stefan Saru
 * 
 * author:	Alec(panovici@elcom.pub.ro)
 * 
 * Obs:
 */

package engine;

import java.util.*;

/**
 * @see engine.SelectionExpressionDescription
 */
class SelectionExpression extends WireSelection
{

	Expression chunks[];
	int n;

	SelectionExpression(NameSpace ns, Vector chunkDescriptions)
	throws ParseException
	{
		super(ns);
		chunks = new Expression[chunkDescriptions.size()];
		Enumeration e = chunkDescriptions.elements();
		n = chunks.length;
		for(int i = 0; i < n ; i++){
			chunks[i] = ((ExpressionDescription) e.nextElement()).instantiate(ns);
			length += chunks[i].length;
			xConsole.debug("chunks[" + i + "].length: " + chunks[i].length); 
		}
	}

	/**
	 * dummy's role is to make constructor signarures differrent
	 */
	SelectionExpression(NameSpace ns, Vector chunksV, int dummy)
	throws ParseException
	{
		super(ns);
		chunks = new Expression[chunksV.size()];
		Enumeration e = chunksV.elements();
		for(int i = 0 ; i < n ; i++){
			chunks[i] = (Expression) e.nextElement();
			length += chunks[i].length;
			xConsole.debug("chunks[" + i + "].length: " + chunks[i].length); 
		}
	}

	public Result evaluate()
	throws InterpretTimeException
	{
		BitVector result = new BitVector(length - 1, 0);
		int curBit = length - 1;
		xConsole.debug("len: " + length);
		for (int i = 0; i < n ; i++) {
			BitVector b = chunks[i].evaluate().getBits();
			result.attrib(b, curBit, curBit-b.n + 1);
			curBit -= b.n;
		}
		return result;
	}

	/**
	 *  Performs the assignement truncating or expanding the rValue
	 *   according to the size of lVal.
	 */
	public void assign(Result r)
	throws InterpretTimeException
	{

		BitVector val = r.getBits(); //this actually makes a copy of r, so
		// the above warning is useless...
		BitVector chunk = new BitVector(val.n-1, 0);
		if(length > val.n ) val.expandS(length); //signed axpansion here
		if(length < val.n ) val.trunc(length);
		int curBit = val.msb;
		for (int i = 0 ; i < n; i++) {
			int nextBit = curBit - (chunks[i].length -1) *  val.increment;
			try {
				try {
					((BitSelect) chunks[i]).set(val, curBit, nextBit);
				} catch (ClassCastException cex) {
					chunk.n = chunks[i].length;
					chunk.attrib(chunk.n - 1, 0, val, curBit,
							nextBit);
					((LeftValue) chunks[i]).assign(chunk);
				}
				curBit = nextBit -  val.increment;
			} catch (ClassCastException cex) {
				throw new InterpretTimeException ("lValue required");
			}
		}

	}

	public void setStrengths(byte[] strengths, int startIndex)
	throws InterpretTimeException
	{
		int curBit = 0;
		for (int i = 0; i < n ; i++) {
			try {
				WireSelection  ws = (WireSelection) chunks[i];
				ws.setStrengths(strengths, curBit);
				curBit += ws.length();
			} catch (ClassCastException cex) { //if not a wireSelection, use default strength
				throw new InterpretTimeException (chunks[i] +
						" cannot be continously assigned: " +
				"not a net");
			}
		}
	}

	/**
	 * if this is a WireSelectionExpression, it
	 * returns the strength & the value of the given
	 * wire bit packed inside an integer: (bit << 16) | strength.
	 * If not, the result is undefined.
	 */
	public int getStrength(int bitIndex) throws ClassCastException {
		int firstBit = length-1, lastBit = length-1;
		xConsole.debug("SelectionExpression.getStrength(" + bitIndex + ")");
		for (int i = 0; i < n ; i++) {
			Expression ck = chunks[i];
			lastBit -= ck.length;
			if((firstBit >= bitIndex) && (bitIndex > lastBit))
				return ((WireSelection)ck).getStrength(bitIndex - lastBit - 1);
			firstBit = lastBit;
		}
		xConsole.warn("strange thigns at SelectionExpression.getStrength");
		return ( BitVector.X << 16) &
		((int)Strength.getStrength(Strength.strong, Strength.strong) & 0x000000ff);
	}

	public void getStrengths(byte s[], int index) {
		int curBit = 0;
		for (int i = 0; i < n ; i++) {
			try {
				WireSelection  ws = (WireSelection) chunks[i];
				ws.getStrengths(s, curBit);
				curBit += ws.length();
			} catch (ClassCastException cex) { //if not a wireSelection, use default strength
				int lastBit = curBit + chunks[i].length;
				byte ds = Strength.defaultStrength;
				while(curBit < lastBit)
					s[curBit++] = ds;
			}
		}
	}

	public String toString(){
		String result = "{";
		int i = 0;
		while (true) {
			result += chunks[i++];
			if (i < n) result += ", ";
			else break;
		}
		result += "}";
		return result;
	}

	void addMonitor(Monitor m){
		for(int i = 0; i < n; i++)
			chunks[i].addMonitor(m);
	}

	public boolean isLeftValue(){
		for (int i = 0 ; i < n ; i++)
			if (!chunks[i].isLeftValue()) return false;
		return true;
	} 

	public int length() {
		return length;
	}

	/**
	 * @see WireSelection.detach
	 */
	void release() {
		for(int i = 0; i < n; i++)
			try {
				((WireSelection) chunks[i]).release();
			} catch (ClassCastException cex){}
	}

	/**
	 * @see WireSelection.reatach
	 */
	void reatach() {
		for(int i = 0; i < n; i++)
			try {
				((WireSelection) chunks[i]).reatach();
			} catch (ClassCastException cex){}
	}

	/**
	 *  @see WireSelection.reatach
	 */
	void breakSelection(WireSelection bits[], int startIndex) {
		for(int i = 0; i < n; i++, startIndex += chunks[i].length)
			try {
				((WireSelection) chunks[i]).breakSelection(bits, startIndex);
			} catch (ClassCastException cex){}
	}

}









